﻿using System;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Configuration;
using System.Web.UI;

namespace Altairis.Web.Configuration.Encryption {
    public class CryptoTaskHandler : IHttpHandler {

        #region Constants

        private const string PROVIDER_NAME = "DataProtectionConfigurationProvider";
        private const string SPLITTER = "<!--|-->";
        private static readonly string[] NON_ENCRYPTABLE_SECTIONS = { 
            "processModel", 
            "runtime", 
            "mscorlib", 
            "startup", 
            "system.runtime.remoting", 
            "configProtectedData", 
            "satelliteassemblies", 
            "cryptographySettings", 
            "cryptoNameMapping", 
            "cryptoClasses" };

        #endregion

        public bool IsReusable {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context) {
            // Setup response headers
            context.Response.ContentType = "text/html";
            context.Response.ContentEncoding = Encoding.UTF8;

            // Get web configuration
            System.Configuration.Configuration config;
            try {
                config = WebConfigurationManager.OpenWebConfiguration(context.Request.ApplicationPath);
            }
            catch (System.Security.SecurityException) {
                // Application is most likely running under medium trust - show error message
                context.Response.Write(Properties.Resources.ErrorPage.Replace("$stylesheet", GetStyleSheetUrl()));
                return;
            }

            // Prepare writer and write header
            var writer = new XhtmlTextWriter(context.Response.Output);
            var pageTemplate = Properties.Resources.PageTemplate.Replace("$stylesheet", GetStyleSheetUrl()).Split(new string[] { SPLITTER }, StringSplitOptions.None);
            writer.Write(pageTemplate[0]);

            // Perform encryption/decryption if requested
            var switchSectionName = context.Request.QueryString["switch"];
            if (!string.IsNullOrEmpty(switchSectionName)) {
                try {
                    var section = config.GetSection(switchSectionName);
                    if (section.SectionInformation.IsProtected) {
                        section.SectionInformation.UnprotectSection();
                        config.Save();
                        writer.Write(Properties.Resources.MessageSectionDecrypted, switchSectionName);
                    }
                    else {
                        section.SectionInformation.ProtectSection(PROVIDER_NAME);
                        config.Save();
                        writer.Write(Properties.Resources.MessageSectionEncrypted, switchSectionName);
                    }
                }
                catch (Exception ex) {
                    writer.Write(Properties.Resources.MessageError, ex.Message);
                }
            }

            // Write current status
            writer.Write(pageTemplate[1]);
            ProcessSectionGroup(writer, config.RootSectionGroup, 0);

            // Write footer
            writer.Write(pageTemplate[2]);
            writer.Close();
        }

        // Helper methods

        private static void ProcessSectionGroup(HtmlTextWriter html, ConfigurationSectionGroup group, int indentLevel) {
            // List all sections
            foreach (ConfigurationSection section in group.Sections) {
                var si = section.SectionInformation;

                // Ignore machine level only sections
                if (si.AllowDefinition == ConfigurationAllowDefinition.MachineOnly || si.AllowDefinition == ConfigurationAllowDefinition.MachineToWebRoot) continue;

                // Display section name
                html.AddAttribute(HtmlTextWriterAttribute.Class, "section");
                html.RenderBeginTag(HtmlTextWriterTag.Tr);
                html.AddAttribute(HtmlTextWriterAttribute.Class, "idl" + indentLevel);
                html.RenderBeginTag(HtmlTextWriterTag.Th);
                html.Write(PrettyPrintName(si.SectionName));
                html.RenderEndTag(); // TH

                // Display status
                if (NON_ENCRYPTABLE_SECTIONS.Contains(si.SectionName)) {
                    // Section cannot be encrypted
                    html.AddAttribute(HtmlTextWriterAttribute.Colspan, "2");
                    html.RenderBeginTag(HtmlTextWriterTag.Td);
                    html.Write(Properties.Resources.SectionStatusNotEncryptable);
                    html.RenderEndTag(); // TD
                }
                else if (si.IsProtected) {
                    // Section is encrypted
                    html.RenderBeginTag(HtmlTextWriterTag.Td);
                    html.Write(Properties.Resources.SectionStatusEncrypted);
                    html.RenderEndTag(); // TD        
                    html.RenderBeginTag(HtmlTextWriterTag.Td);
                    html.AddAttribute(HtmlTextWriterAttribute.Href, "?switch=" + si.SectionName);
                    html.RenderBeginTag(HtmlTextWriterTag.A);
                    html.Write(Properties.Resources.OperationDecrypt);
                    html.RenderEndTag(); // A
                    html.RenderEndTag(); // TD
                }
                else {
                    // Section is not encrypted
                    html.RenderBeginTag(HtmlTextWriterTag.Td);
                    html.Write(Properties.Resources.SectionStatusOpen);
                    html.RenderEndTag(); // TD        
                    html.RenderBeginTag(HtmlTextWriterTag.Td);
                    html.AddAttribute(HtmlTextWriterAttribute.Href, "?switch=" + si.SectionName);
                    html.RenderBeginTag(HtmlTextWriterTag.A);
                    html.Write(Properties.Resources.OperationEncrypt);
                    html.RenderEndTag(); // A
                    html.RenderEndTag(); // TD
                }
                html.RenderEndTag(); // TR                
            }

            // List all sub-groups
            foreach (ConfigurationSectionGroup subGroup in group.SectionGroups) {
                html.AddAttribute(HtmlTextWriterAttribute.Class, "group");
                html.RenderBeginTag(HtmlTextWriterTag.Tr);
                html.AddAttribute(HtmlTextWriterAttribute.Class, "idl" + indentLevel);
                html.AddAttribute(HtmlTextWriterAttribute.Colspan, "3");
                html.RenderBeginTag(HtmlTextWriterTag.Th);
                html.Write(PrettyPrintName(subGroup.SectionGroupName));
                html.RenderEndTag(); // TH
                html.RenderEndTag(); // TR

                ProcessSectionGroup(html, subGroup, indentLevel + 1);
            }
        }

        private static string PrettyPrintName(string name) {
            if (name.IndexOf('/') == -1) return name;
            return name.Substring(name.LastIndexOf('/') + 1);
        }

        private static string GetStyleSheetUrl() {
            using (var p = new Page()) {
                return p.ClientScript.GetWebResourceUrl(typeof(CryptoTaskHandler), "Altairis.Web.Configuration.Encryption.Resources.styles.css");
            }
        }

    }
}
